﻿using Inaction.DbSchame;
using Inaction.Mapping;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using Inaction.Rule;
using Inaction.Message;

namespace Inaction
{
    [Serializable]
    public abstract class DbProvider 
    {
        #region 属性
        static DbProvider instance = null;

        protected bool connected = false;

        protected static Action initProviderAction = null;

        static DbProvider GetProvider()
        {
            return Inaction.DbProvider.Instance;
        }
       
        internal protected static DbProvider Instance
        {
            get
            {
                if (instance == null || !instance.connected)
                {
                    MessageHost.ShowMessage("正在加载数据结构信息");
                    if (Inaction.AppConfig.Instance.RunLocation == RunLocation.Client)
                    {
                        instance = RemotingMethod.Invoke<DbProvider>(new Func<DbProvider>(GetProvider));
                    }
                    else
                    {
                        instance = ServiceContainer.GetService<DbProvider>();
                    }
                    try
                    {
                        if (Inaction.AppConfig.Instance.RunLocation != RunLocation.Client)
                        {
                            using (var conn = instance.CreateConnection())
                            {
                                conn.Open(); 
                                conn.Close();
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        instance = null;
                        throw e;
                    }
                    MessageHost.ShowMessage("");
                }
                return instance;
            }
            set { instance = value; }
        }

        public abstract SqlType SqlType { get; }
        public IDbConnection Connection
        {
            get
            {
                var con = Instance.CreateConnection();
                if (con.State != ConnectionState.Open) con.Open();
                return con;
            }
        }
        public abstract string ParaFix { get; }
        public List<Table> Tables { get; protected set; }
        public List<View> Views { get; protected set; }
        internal virtual int MaxParaCount { get { return 2000; } }

        #endregion

        #region 方法

        #region 抽象方法
        public abstract void ParseException(Exception ex);

        internal Table GetTable(string tableName)
        {
            return Tables.FirstOrDefault(p => p.Name.Equals(tableName, StringComparison.OrdinalIgnoreCase));
        }

        internal View GetView(string viewName)
        {
            return Views.FirstOrDefault(p => p.Name.Equals(viewName, StringComparison.OrdinalIgnoreCase));
        }
        internal virtual string ParseBatchSql(string sql) { return sql; }

        Func<string, string> nameFormater = (x) => { return x; };

        string nameFormatString = "{0}";
        public virtual Func<string, string> NameFormater
        {
            get { return nameFormater; }
            set { nameFormater = value; }
        }
        internal string FormatName(string name)
        {
            return NameFormater(name);
        }

        internal Column GetColumn(string tableName, string columnName)
        {
            var table = GetTable(tableName);
            if (table == null) return null;
            return table.Columns.FirstOrDefault(p => p.Name.Equals(columnName, StringComparison.OrdinalIgnoreCase));
        }
        protected abstract IDbConnection CreateConnection();
        internal protected abstract string GetPageSql(Type type, int pageSize, int pageIndex);

        #endregion

        #region 数据访问

        #region CreateCommand
        internal static IDbCommand CreateCommand(IDbConnection connection, SqlStatement statement)
        {
            var command = connection.CreateCommand();
            command.CommandText = statement.SqlText;
            command.CommandType = CommandType.Text;
            if (statement.ParaValues.Count != 0)
            {
                foreach (var param in statement.ParaValues)
                {
                    AddParameter(command, param.Name, param.Value);
                }
            }
            return command;
        }

        internal static IDbCommand CreateCommand(IDbTransaction transtaction, SqlStatement statement, IDbCommand command = null)
        {
            if (command == null)
            {
                command = transtaction.Connection.CreateCommand();
                command.Transaction = transtaction;
            }
            command.CommandText = statement.SqlText;
            command.CommandType = CommandType.Text;
            if (statement.ParaValues.Count != 0)
            {
                foreach (var param in statement.ParaValues)
                {
                    AddParameter(command, param.Name, param.Value);
                }
            }
            return command;
        }

        internal static IDbCommand CreateBatchCommand(IDbTransaction transtaction, IDbCommand command, SqlStatement statement)
        {
            if (command == null)
            {
                command = transtaction.Connection.CreateCommand();
                command.Transaction = transtaction;
            }
            command.CommandText += statement.SqlText + ";" + Environment.NewLine;
            foreach (var param in statement.ParaValues)
            {
                AddParameter(command, param.Name, param.Value);
            }
            return command;
        }

        public static IDbCommand CreateCommand(string sqlText, IDbDataParameter[] dbParams, CommandType cmdType)
        {
            var command = Instance.Connection.CreateCommand();
            command.CommandText = sqlText;
            command.CommandType = cmdType;
            if (dbParams != null)
            {
                foreach (IDbDataParameter param in dbParams)
                    command.Parameters.Add(param);
            }
            return command;
        }

        public static IDbCommand CreateCommand(string sqlText, IDbDataParameter[] dbParams)
        {
            return CreateCommand(sqlText, dbParams, CommandType.Text);
        }

        public static IDbCommand CreateCommand(string sqlText, CommandType cmdType)
        {
            return CreateCommand(sqlText, null, cmdType);
        }

        public static IDbCommand CreateCommand(string sqlText)
        {
            return CreateCommand(sqlText, null, CommandType.Text);
        }

        public static IDbCommand CreateCommand(IDbTransaction transtaction, string sqlText, IDbDataParameter[] dbParams, CommandType cmdType)
        {
            var command = transtaction.Connection.CreateCommand();
            command.Transaction = transtaction;
            command.CommandText = sqlText;
            command.CommandType = cmdType;
            if (dbParams != null)
            {
                foreach (IDbDataParameter param in dbParams)
                {
                    command.Parameters.Add(param);
                }
            }
            return command;
        }

        public static IDbCommand CreateCommand(IDbTransaction transtaction, string sqlText, IDbDataParameter[] dbParams)
        {
            return CreateCommand(transtaction, sqlText, dbParams, CommandType.Text);
        }

        public static IDbCommand CreateCommand(IDbTransaction transtaction, string sqlText, CommandType cmdType)
        {
            return CreateCommand(transtaction, sqlText, null, cmdType);
        }

        public static IDbCommand CreateCommand(IDbTransaction transtaction, string sqlText)
        {
            return CreateCommand(transtaction, sqlText, null, CommandType.Text);
        }

        #endregion

        #region AddParameter
        public static IDbDataParameter AddParameter(IDbCommand command, string paramName, object paramValue, ParameterDirection direction = ParameterDirection.Input)
        {
            IDbDataParameter param = command.CreateParameter();
            if (paramName.StartsWith(Instance.ParaFix))
            {
                param.ParameterName = paramName;
            }
            else
            {
                param.ParameterName = Instance.ParaFix + paramName;
            }
            param.Value = paramValue;
            param.Direction = direction;
            command.Parameters.Add(param);
            return param;
        }

        internal static IDbDataParameter AddParameter(IDbCommand command, SqlStatement sql, ParameterDirection direction = ParameterDirection.Input)
        {
            IDbDataParameter param = command.CreateParameter();
            foreach (var paraValue in sql.ParaValues)
            {
                param.ParameterName = paraValue.Name;
                param.Value = paraValue.Value;
                param.Direction = direction;
                command.Parameters.Add(param);
            }
            return param;
        }
        #endregion

        #endregion

        public static void SetInstance(DbProvider p)
        {
            instance = p;
        }
        #endregion
    }
}
